package library;

import java.util.LinkedList;
import java.util.List;

public class Geometry {

	//Great circle distance
	//Shortest distance on a sphere
	//Math.acos(Math.sin(lat1)*Math.sin(lat2)+Math.cos(lat1)*Math.cos(lat2)*Math.cos(long1-long2))*rad;

	class Line{
		double m,b;
	}
	class LineSegment extends Line{
		Point start,end,center,mid;
		double length, angle;
		public LineSegment(Point start, Point end){
			this.start=start;
			this.end=end;

			angle = Math.atan2(end.y-start.y,end.x-start.x);
			m = (end.y-start.y)/(end.x-start.x);
			b = start.y-start.x*m;
			length = distance(start, end);
			center = new Point((start.x+end.x)/2,(start.y+end.y)/2);
			mid = new Point((start.x+end.x)/2,(start.y+end.y)/2);
		}
		//WORKS
		//NOTE: WILL NOT CHECK IF TWO SEGMENTS ACTUALLY INTERSECT.  ONLY IF THEY ARE FACING THE SAME DIRECTION ON THE SAME LEVEL
		public boolean intersectsAtLine(LineSegment other){
			if(Math.abs(other.angle - angle)%Math.PI>1e-6)
				return false;
			if(Math.abs(Math.abs(angle)-Math.PI/2)<1e-6)
				return Math.abs(start.x-other.start.x) <1e-6;
			else
				return Math.abs(b-other.b)<1e-6;
		}
		
		//WORKS
		public boolean isParallel(LineSegment other){
			if(Math.abs(other.angle - angle)%Math.PI>1e-6)
				return false;
			if(Math.abs(Math.abs(angle)-Math.PI/2)<1e-6)
				return Math.abs(start.x-other.start.x) >1e-6;
				else
					return Math.abs(b-other.b)>1e-6;
		}
		/**
		 * Assumes they are not parallel
		 * WORKS
		 * @param other
		 * @return
		 */
		public Point getInterPoint(LineSegment other){
			double x,y;
			if(Math.abs(Math.abs(angle)-Math.PI/2)<1e-6){
				x = start.x;
				y = other.b+x*Math.tan(other.angle);
			}
			else if(Math.abs(Math.abs(other.angle)-Math.PI/2)<1e-6){
				x = other.start.x;
				y = b+x*Math.tan(angle);
			}
			else{
				x = (b-other.b)/(Math.tan(other.angle)-Math.tan(angle));
				y= b+x*Math.tan(angle);
			}
			if(Math.abs(x)<1e-6)
				x=0;
			if(Math.abs(y)<1e-6)
				y=0;
			return new Point(x,y);
		}
		
		//Includes the end points.
		//WORKS!
		public boolean isPointOnLine(Point p){
			return Math.abs(length-(distance(p,start)+distance(p,end))) < 1e-6;
		}
	}

	class Point{
		double x,y;
		public Point (double x,double y){ this.x =x; this.y=y; }
	}
	
	//WORKS!
	public static double distance(Point p1, Point p2){
		return Math.sqrt(Math.pow(p1.y-p2.y, 2)+Math.pow(p1.x-p2.x, 2));
	}
	
	// Find the distance between a point p and the line passing between points
	// a and b.
	public double pointLineDist(Point a, Point b, Point p) {
		if (Math.abs(a.x - b.x) < 1e-6) {
			return Math.abs(p.x - a.x);
		}
		if (Math.abs(a.y - b.y) < 1e-6) {
			return Math.abs(p.y - a.y);
		}
		
		double top = Math.abs((b.x-a.x)*(a.y-p.y) - (a.x-p.x)*(b.y-a.y));
		double bottom = Math.sqrt((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y));
		return Math.abs(top / bottom);
	}
	
	static class Circle{
		double radius;
		Point center;
		public Circle(double radius, Point center){ this.radius=radius; this.center=center; }
	}
	static class Polygon{
		List<Point> points;
		public Polygon(){ points = new LinkedList<Point>(); }
	}
	class Ray extends Line{
		Point start;
		double orientation; //Radians
		public Ray(Point start, double orientation){ this.start=start; this.orientation=orientation; }
	}
	static class CircleIntersection{
		Point upper,lower;
		public CircleIntersection(Point upper,Point lower){ this.upper=upper; this.lower=lower; }
	}

	//WORKS!
	Point getCircleCenter (Point[] points)
	{
		//Slope from 1 to 2
		double m3 = new LineSegment(points[0], points[1]).m;
		//Slope from 2 to 3
		double m4 = new LineSegment(points[1], points[2]).m;

		Point mid3 = new LineSegment(points[0], points[1]).mid;
		Point mid4 = new LineSegment(points[1], points[2]).mid;
		//The slope of the line that is perpendicular to the line from 1 to 2 
		double m1 = -1/m3;
		//The slope of the line that is perpendicular to the line from 2 to 3
		double m2 = -1/m4;

		double b1 = mid3.y-m1*mid3.x;
		double b2 = mid4.y-m2*mid4.x;

		double x = (b2-b1)/(m1-m2);
		double y = m1*x+b1;

		if (m3==0){
			x=mid3.x;
			y = m2*x+b2;
		}
		if (m4==0){
			x=mid4.x;
			y = m1*x+b1;
		}

		if(Math.abs(x)<1e-6)
			x=0;
		if(Math.abs(y)<1e-6)
			y=0;

		Point centerPoint = new Point(x,y);
		return centerPoint;
	}

	/*
	//Note: null=do not intersect.  If there is only one intersection point, upper and lower will be equal
	public CircleIntersection lineCircleIntersection(Line l, Circle c){
	}

	//Includes edges as well as inside
	public boolean isPointOnPolygon(Point p, Polygon Poly){

	}
	//Just the inside
	public boolean isPointInPolygon(Point p, Polygon Poly){

	}
	//Note: null if intersection in empty
	public Polygon polygonIntersection( Polygon p1, Polygon p2){

	}
	 */
}
